package com.github.aloxc.plugin.restplus.listener;

import com.github.aloxc.plugin.restplus.i18n.I18Bundle;
import com.github.aloxc.plugin.restplus.common.Consts;
import com.github.aloxc.plugin.restplus.utils.StringUtils;
import com.github.aloxc.plugin.restplus.entity.HistoryTreeNode;
import com.github.aloxc.plugin.restplus.entity.RequestHistory;
import com.github.aloxc.plugin.restplus.component.TextPane;
import com.github.aloxc.plugin.restplus.test.TestResult;
import com.intellij.openapi.editor.colors.FontPreferences;
import com.intellij.openapi.project.Project;
import org.apache.commons.io.FileUtils;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;

/**
 * @author leero
 */
public class HistoryTreeSelectionListener implements TreeSelectionListener {
    private Project project;
    private TextPane historyDetailTextPane;
    public HistoryTreeSelectionListener(Project project,TextPane historyDetailTextPane){
        this.project = project;
        this.historyDetailTextPane = historyDetailTextPane;
        initStyle();
    }

    private void initStyle() {
        Style normal = historyDetailTextPane.getStyledDocument().addStyle(null, null);
        StyleConstants.setFontFamily(normal, "Monospaced");
        StyleConstants.setFontSize(normal, fontSize);

        Style normalStyle = historyDetailTextPane.addStyle("normal", normal);
        Style redStyle = historyDetailTextPane.addStyle("red", normal);
        Style mainStyle = historyDetailTextPane.addStyle("main", normal);
        Style greenStyle = historyDetailTextPane.addStyle("green", normal);

        StyleConstants.setForeground(redStyle, Color.RED);
        StyleConstants.setForeground(mainStyle, Consts.MAIN_COLOR);
        StyleConstants.setForeground(greenStyle, Color.GREEN);

    }

    @Override
    public void valueChanged(TreeSelectionEvent e) {
        // 按照鼠标点击的坐标点获取路径
        HistoryTreeNode node = (HistoryTreeNode) ((JTree)e.getSource()).getLastSelectedPathComponent();
        if(node == null || StringUtils.isNullOrEmpty(node.getFilePath()) ){
            return;
        }
        try {
            String moduleName = node.getModuleName();
            String content = FileUtils.readFileToString(new File(node.getFilePath()), "utf-8");
            RequestHistory history = RequestHistory.fromJson(content);
            historyDetailTextPane.setText("");
            final Document docs = historyDetailTextPane.getDocument();
            try {
                docs.insertString(docs.getLength(), "Url : ", historyDetailTextPane.getStyle("normal"));
                docs.insertString(docs.getLength(), history.getUrl(), historyDetailTextPane.getStyle("main"));

                docs.insertString(docs.getLength(), "\nMethod : ", historyDetailTextPane.getStyle("normal"));
                docs.insertString(docs.getLength(), history.getHttpMethod().name(), historyDetailTextPane.getStyle("main"));

                if(history.getHeaderMap() != null) {
                    docs.insertString(docs.getLength(), "\nHeaders : ", historyDetailTextPane.getStyle("normal"));
                    history.getHeaderMap().forEach((k, v) -> {
                        try {
                            docs.insertString(docs.getLength(), "\n   " + k + " : ", historyDetailTextPane.getStyle("normal"));
                            docs.insertString(docs.getLength(), v, historyDetailTextPane.getStyle("main"));
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    });
                }

                if(history.getCookieMap() != null) {
                    docs.insertString(docs.getLength(), "\nRequest cookies : ", historyDetailTextPane.getStyle("normal"));
                    history.getCookieMap().forEach((k, v) -> {
                        try {
                            docs.insertString(docs.getLength(), "\n   " + k + " : ", historyDetailTextPane.getStyle("normal"));
                            docs.insertString(docs.getLength(), v, historyDetailTextPane.getStyle("main"));
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    });
                }

                if(history.getParamMap() != null) {
                    docs.insertString(docs.getLength(), "\nRequest parameters : ", historyDetailTextPane.getStyle("normal"));
                    history.getParamMap().forEach((k, v) -> {
                        try {
                            docs.insertString(docs.getLength(), "\n   " + k + " : ", historyDetailTextPane.getStyle("normal"));
                            docs.insertString(docs.getLength(), v, historyDetailTextPane.getStyle("main"));
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    });
                }
                if(history.getBody() != null) {
                    docs.insertString(docs.getLength(), "\nRequest body : ", historyDetailTextPane.getStyle("normal"));
                    docs.insertString(docs.getLength(), history.getBody(), historyDetailTextPane.getStyle("main"));
                }
                docs.insertString(docs.getLength(), "\nResponse status code : ", historyDetailTextPane.getStyle("normal"));
                docs.insertString(docs.getLength(), String.valueOf(history.getStatusCode()), historyDetailTextPane.getStyle(history.getStatusCode() == 200 ? "main" : "red"));

                if(StringUtils.isNullOrEmpty(history.getMessage())) {
                    docs.insertString(docs.getLength(), "\nResponse content : ", historyDetailTextPane.getStyle("normal"));
                    docs.insertString(docs.getLength(), "\n", historyDetailTextPane.getStyle("main"));
                    format(history.getContent());
                }else {
                    docs.insertString(docs.getLength(), "\nException message : ", historyDetailTextPane.getStyle("normal"));
                    docs.insertString(docs.getLength(), "\n" + history.getMessage(), historyDetailTextPane.getStyle("red"));
                }
                if(history.getTestResultList() != null){
                    long passed = history.getTestResultList().stream().filter(TestResult::isSuccess).count();
                    docs.insertString(docs.getLength(), "\nTests result : ", historyDetailTextPane.getStyle("normal"));
                    docs.insertString(docs.getLength(), I18Bundle.getMessage("com.github.aloxc.plugin.restplus.test.result.passed.text") + "  " + passed + "  ", historyDetailTextPane.getStyle("green"));
                    docs.insertString(docs.getLength(), String.format(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.test.result.failed.text"),history.getTestResultList().size() - passed),
                            historyDetailTextPane.getStyle("red"));

                    history.getTestResultList().forEach(result -> {
                        try {
                            docs.insertString(docs.getLength(), "\n   " + result.getName() + " : ", historyDetailTextPane.getStyle("normal"));
                            docs.insertString(docs.getLength(),
                                    result.isSuccess() ? I18Bundle.getMessage("com.github.aloxc.plugin.restplus.test.result.passed.text") : I18Bundle.getMessage("com.github.aloxc.plugin.restplus.test.result.failed.text1") + "  " + result.getMessage(),
                                    result.isSuccess() ? historyDetailTextPane.getStyle("green") : historyDetailTextPane.getStyle("red"));
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    });
                }
                if(!StringUtils.isNullOrEmpty(history.getTest())) {
                    docs.insertString(docs.getLength(), "\nTests : ", historyDetailTextPane.getStyle("normal"));
                    docs.insertString(docs.getLength(), "\n" + history.getTest(), historyDetailTextPane.getStyle("main"));
                }

                historyDetailTextPane.setCaretPosition(docs.getLength());
            } catch (BadLocationException ex) {
                ex.printStackTrace();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }


    private SimpleAttributeSet keyAttr;
    private SimpleAttributeSet valueAttr;
    private SimpleAttributeSet normalAttr;
    private Color lineBackgroundColor;
    String lastKeyStr = null;
    private int fontSize = 14;
    boolean isWrapRow = false;
    boolean hasExists = false;
    private static final String FOUR_BLANK_STR = "  ";
    private String fontFamily ;
    int nowSeg = 0;
    int nowSegStart = 0;
    public void format(String old) {
        this.nowSegStart = 0;
        this.lastKeyStr = null;
        this.isWrapRow = false;
        this.hasExists = false;
        this.keyAttr = new SimpleAttributeSet();
        this.valueAttr = new SimpleAttributeSet();
        this.normalAttr = new SimpleAttributeSet();
        FontPreferences fontPreferences = getFontPreferences();
        fontFamily = fontPreferences.getFontFamily();

        StyleConstants.setForeground(this.keyAttr, Consts.MAIN_COLOR);
        StyleConstants.setFontSize(this.keyAttr, fontSize);
        StyleConstants.setFontFamily(this.keyAttr, fontFamily);

        Color fc = new Color(171,15,58);
        StyleConstants.setForeground(this.normalAttr, Color.gray);
        StyleConstants.setFontSize(this.normalAttr, fontSize);
        StyleConstants.setFontFamily(this.normalAttr,fontFamily);

        StyleConstants.setForeground(this.valueAttr,fc);
        StyleConstants.setFontSize(this.valueAttr, fontSize);
        StyleConstants.setFontFamily(this.valueAttr, fontFamily);

        StyledDocument doc = historyDetailTextPane.getStyledDocument();
        String[] lines = old.split("\n");
        int nowTabCount = 0;
        this.isWrapRow = false;
        this.hasExists = false;
        StringBuffer tempStr = new StringBuffer();
        int count = lines.length;

        for(int i = 0; i < count; ++i) {
            String line = lines[i];
            nowTabCount = this.formatSingleLine(doc, line.trim(), nowTabCount, tempStr);
        }
        if (tempStr.length() != 0) {
            try {
                doc.insertString(doc.getLength(), tempStr.toString(), (AttributeSet)null);
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
        }
    }

    private final FontPreferences getFontPreferences() {
        return new FontPreferences();
    }
    private int formatSingleLine(StyledDocument doc, String line, int nowTabCount, StringBuffer tempStr) {
        boolean isLastBlank = false;

        for (int i = 0; i < line.length(); i++) {
            char ch = line.charAt(i);
            switch (ch) {
                case '[':
                case '{':
                    tempStr.append(ch);
                    tempStr.append("\n");
                    nowTabCount++;
                    printBlanks(tempStr, nowTabCount);
                    isLastBlank = true;
                    try {
                        doc.insertString(doc.getLength(), tempStr.toString(), this.normalAttr);
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                    tempStr.delete(0, tempStr.length());

                    this.isWrapRow = true;
                    this.hasExists = false;
                    break;
                case ']':
                case '}':
                    nowTabCount--;
                    try {
                        doc.insertString(doc.getLength(), tempStr.toString(), this.valueAttr);
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                    tempStr.delete(0, tempStr.length());

                    tempStr.append("\n");
                    printBlanks(tempStr, nowTabCount);
                    tempStr.append(ch);
                    isLastBlank = true;
                    try {
                        doc.insertString(doc.getLength(), tempStr.toString(), this.normalAttr);
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                    tempStr.delete(0, tempStr.length());

                    this.isWrapRow = true;
                    this.hasExists = false;
                    break;
                case '\n':
                    break;
                case ',':
                    try {
                        doc.insertString(doc.getLength(), tempStr.toString(), this.valueAttr);
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                    tempStr.delete(0, tempStr.length());
                    tempStr.append(ch);
//                    if(i == line.length() -1) {
                    tempStr.append("\n");
                    printBlanks(tempStr, nowTabCount);
//                    }
                    try {
                        doc.insertString(doc.getLength(), tempStr.toString(), this.normalAttr);
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                    tempStr.delete(0, tempStr.length());
                    this.isWrapRow = true;
                    this.hasExists = false;
                    isLastBlank = true;
                    break;
                case ':':
                case '=':
                    if (!this.isWrapRow && this.hasExists) {
                        tempStr.append(ch);
                    } else {
                        try {
                            doc.insertString(doc.getLength(), tempStr.toString(), this.keyAttr);
                            doc.insertString(doc.getLength(), String.valueOf(ch), this.normalAttr);
                        } catch (BadLocationException e) {
                            e.printStackTrace();
                        }
                        tempStr.delete(0, tempStr.length());
                    }
                    this.isWrapRow = false;
                    this.hasExists = true;
                    break;
                case ' ':
                    if (isLastBlank) {
                        break;
                    }
                default:
                    tempStr.append(ch);
                    isLastBlank = false;
                    break;
            }
        }
        return nowTabCount;
    }

    private static void printBlanks(StringBuffer buffer, int tabCount) {
        for (int j = 0; j < tabCount; j++) {
            buffer.append(FOUR_BLANK_STR);
        }
    }
}
